#pragma once

#include "../base/obj_pointer.h"
#include <unordered_map>
#include <vector>

USE_JLIB_NS


struct Options {

  constexpr static int NonValue = 1;
  constexpr static int Required = 2;

  struct Def {
    const char* name;
    const char* desc;
    int flag;
    char tag;
  };

  void operator() (int argc, char** argv) noexcept;
  void operator() (const std::vector<std::string>& args) noexcept;

  bool operator() (char tag) const noexcept;
  bool operator() (const std::string& name) const noexcept;

  const char* operator[] (size_t index) const noexcept;
  const char* operator[] (char tag) const noexcept;
  const char* operator[] (const std::string& name) const noexcept;

  Options (const std::initializer_list<Def>& defs) noexcept;
  Options (int argc, char** argv, const std::initializer_list<Def>& defs) noexcept;
  Options (const std::vector<std::string>& args, const std::initializer_list<Def>& defs) noexcept;

public:
  std::unordered_map<char, const Def&> _tags;
  std::unordered_map<std::string, const Def&> _names;
  std::unordered_map<char, std::vector<std::string>> _map_by_tag;
  std::unordered_map<std::string, std::vector<std::string>> _map_by_name;
  std::vector<std::string> _unnamed;
  std::string _name_cache;
  char _tag_cache;

  char __valid_tag (char tag) noexcept;
  bool __valid_name (const char* name) noexcept;

  void _add_by_tag (char tag, const char* value) noexcept;
  void _add_by_name (const std::string& name, const char* value) noexcept;
  void _add_unnamed (const char* value) noexcept;

  void _collect () noexcept;
  void _handle_arg (const char* arg) noexcept;
};
